package com.cantor.common.util;

import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.net.url.UrlBuilder;

import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.stream.Collectors;

public class CantorUtil {

    /**
     * 传入hutool.UrlBuilder, 经过特殊改造处理, 返回如下格式的字符串, 用于注册到远程注册中心节点(不带"/")
     * 192.168.1.9:8868?version=&loadBalance=&timeout=&weight=&retries=&mock=&interface=com.xxx.xxx&methods=fun1,fun2
     *
     * @param builder hutool工具包的url拼接工具.
     * @return
     */
    public static String buildNodePath(UrlBuilder builder) {
        StringBuffer sb = new StringBuffer();
        String decodedQueryStr = new String(URLDecoder.decode(builder.getQueryStr().getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
        sb.append(builder.getHost())
                .append(":")
                .append(builder.getPort())
                .append("?")
                .append(decodedQueryStr);
        return sb.toString();
    }

    /**
     * 提供下面这样的url,得到一个UrlBuilder, 你可以调用它的getHost, getPort, getQuery().get(key)获取参数信息
     * 192.168.1.9:8868?version=&loadBalance=&timeout=&weight=&retries=&mock=&interface=com.xxx.xxx&methods=fun1,fun2
     *
     * @param specialPath 前面不带"http://"的特殊url
     * @return
     */
    public static UrlBuilder getUrlBuilder(String specialPath) {
        if ("/".equals(specialPath.toCharArray()[0])) {
            specialPath = specialPath.substring(1);
        }
        return UrlBuilder.of("http://" + specialPath);
    }


    /**
     * 传入一个Class, 返回一个由它所有方法名组成的数组拼接成的字符串, 间隔符默认是","
     *
     * @param clz       类或接口的class
     * @param delimiter 分隔符
     * @return
     */
    public static String getMethodNamesStr(Class clz, String delimiter) {
        // 排序一下,防止每次反射得到的方法名顺序不一样
        return Arrays.stream(clz.getDeclaredMethods()).map(Method::getName).sorted().collect(Collectors.joining(delimiter));
    }

    public static String getMethodNamesStr(Class clz) {
        return getMethodNamesStr(clz, ",");
    }


    /**
     * 线程休眠专用方法, 统一管理, 不用每次在代码里try catch
     * @param timeout   超时时间毫秒数
     */
    public static void threadSleep(int timeout) {
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 恭喜你发现一个彩蛋
     * 这个方法你可以用来在控制台打印Cantor RPC框架的LOGO
     */
    public static void printCantorLogo(){
        System.out.println("\n\n");
        System.out.println(
                "   ██████                       ██                  \n" +
                "  ██░░░░██                     ░██                  \n" +
                " ██    ░░   ██████   ███████  ██████  ██████  ██████\n" +
                "░██        ░░░░░░██ ░░██░░░██░░░██░  ██░░░░██░░██░░█\n" +
                "░██         ███████  ░██  ░██  ░██  ░██   ░██ ░██ ░ \n" +
                "░░██    ██ ██░░░░██  ░██  ░██  ░██  ░██   ░██ ░██   \n" +
                " ░░██████ ░░████████ ███  ░██  ░░██ ░░██████ ░███   \t---Tu");
        System.out.println("\n\n");
    }

    /**
     * 获取本机可用的地址
     */
    public static String getExactLocalHost() {
        try {
            InetAddress candidateAddress = null;

            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface iface = networkInterfaces.nextElement();
                // 该网卡接口下的ip会有多个，也需要一个个的遍历，找到自己所需要的
                for (Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) {
                    InetAddress inetAddr = inetAddrs.nextElement();
                    // 排除loopback回环类型地址（不管是IPv4还是IPv6 只要是回环地址都会返回true）
                    if (!inetAddr.isLoopbackAddress()) {
                        if (inetAddr.isSiteLocalAddress()) {
                            // 如果是site-local地址，就是它了 就是我们要找的
                            // ~~~~~~~~~~~~~绝大部分情况下都会在此处返回你的ip地址值~~~~~~~~~~~~~
                            return inetAddr.getHostAddress();
                        }

                        // 若不是site-local地址 那就记录下该地址当作候选
                        if (candidateAddress == null) {
                            candidateAddress = inetAddr;
                        }

                    }
                }
            }

            // 如果出去loopback回环地之外无其它地址了，那就回退到原始方案吧
            return candidateAddress == null ? InetAddress.getLocalHost().getHostAddress() : candidateAddress.getHostAddress();
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("CantorUtil.getExactLocal()获取本机地址时出现异常");
            return null;
        }
    }

}
